<?php
/**
 *
 * 公共模型基类
 *
 * @author     liang<lang@vip.deyi.com>
 * @since      2015年5月21日
 * @copyright  deyi.com
 */

namespace Apps\Common\Model;

use Apps\Common\Constant\CacheKeyConstant;
use Apps\Common\Constant\CacheTimeoutConstant;
use Apps\Common\Constant\ExceptionConstant;
use Deyi\Functions\CacheFunction;
use Phalcon\Crypt;
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Resultset\Simple as ResultSet;

/**
 * Class BaseModel
 * @package Apps\Common\Model
 */
class BaseModel extends Model
{

    protected static $_instance = [];

    protected static $_propCache = [];

    protected static $_charset = [];

    protected static $_DBNAME = null;

    protected static $_TABLENAME = null;

    /**
     * @var $memcached \Memcached
     */
    protected static $_memcached = null;

    /**
     * @var $config
     */
    protected static $_config = null;

    /**
     * @var $redis \Redis
     */
    protected static $_redis = null;

    /**
     * redis非序列化访问驱动
     * @var \Redis
     */
    protected static $_ns_redis;

    /**
     * @var $_logger \Apps\Common\Logger\FileLogger
     */
    protected static $_logger = null;

    /**
     * 是否开启事务
     * @var bool
     */
    protected static $openTransaction = false;

    /**
     * 开启事务后的数据库连接池
     * @var \Phalcon\Db\AdapterInterface[]
     */
    protected static $transConnPool = [];

    /**
     * DB数据库读连接资源服务名数组
     * @var array
     */
    protected static $_dbReadConServiceName = [];

    /**
     * DB数据库写连接资源池
     * @var \Phalcon\Db\AdapterInterface[]
     */
    protected static $_dbWriteConPool = [];

    /**
     * 是否强制读走master
     * @var bool
     */
    protected static $_forceMasterRead = false;

    /**
     * @var Crypt
     */
    protected static $_crypt = null;

    public function initialize()
    {
        if (!is_null(static::$_TABLENAME) && !is_null(static::$_DBNAME)) {

            // 设定数据库主从连接
            $this->setWriteConnectionService('db_' . static::$_DBNAME . '_master');

            if (!isset(static::$_dbWriteConPool[static::$_DBNAME])) {
                static::$_dbWriteConPool[static::$_DBNAME] = $this->getWriteConnection();
            }

            //判断slave出错则跳过slave从库连接主库
            if (apcu_fetch(CacheKeyConstant::DB_SLAVE_SKIP_CACHE_KEY)) {
                static::$_forceMasterRead = true;
            }

            $conServiceName = 'db_' . static::$_DBNAME . '_slave';
            if (isset(static::$_dbReadConServiceName[static::$_DBNAME])) {
                $conServiceName = static::$_dbReadConServiceName[static::$_DBNAME];
            } else {
                //获取百分比计算主数据库读的概率,如果开启了事务，连接全部走主库
                if (static::$_forceMasterRead ||
                    static::$openTransaction ||
                    mt_rand(1, 100) <= (int)$this->getDI()->get('config')['mysql'][strtolower(static::$_DBNAME) . '_master']->read
                ) {
                    $conServiceName = 'db_' . static::$_DBNAME . '_master';
                }
                static::$_dbReadConServiceName[static::$_DBNAME] = $conServiceName;
            }

            $this->setReadConnectionService($conServiceName);

            //判断是否开启事务
            if (static::$openTransaction && !isset(static::$transConnPool[static::$_DBNAME])) {
                static::$transConnPool[static::$_DBNAME] = $this->getWriteConnection();
                if (!static::$transConnPool[static::$_DBNAME]->begin()) {
                    throw new \Exception('begin db transaction failed', ExceptionConstant::INTERNAL_ERROR);
                }
            }

            // 提取数据库字符编码参数
            $this->setSource(static::$_TABLENAME);

            if (!isset(static::$_charset[static::$_DBNAME])) {
                static::$_charset[static::$_DBNAME] = strtolower(
                    $this->getDI()->get('config')['mysql:master:' . strtolower(static::$_DBNAME)]->charset
                );
            }
        }

        // 初始化memcached，redis, config资源
        if (is_null(static::$_config)) {
            static::$_memcached = $this->getDI()->get('memcached');
            static::$_redis = $this->getDI()->get('redis');
            static::$_config = $this->getDI()->get('config');
            static::$_logger = $this->getDI()->get('logger');
            static::$_ns_redis = $this->getDI()->get('redis_none_serializer');
            static::$_crypt = $this->getDI()->get('crypt');
        }
    }

    /**
     *
     * 单例模式调用
     *
     * @author liang<lang@vip.deyi.com>
     * @return static
     */
    public static function getInstance()
    {

        $class = get_called_class();
        if (!isset(static::$_instance[$class])) {
            return static::$_instance[$class] = new $class;
        }

        return static::$_instance[$class];
    }

    /**
     * 设置读是否强制走master读
     *
     * @author liang<lang@vip.deyi.com>
     * @param $bool
     */
    public static function setForceMasterRead($bool)
    {
        static::$_forceMasterRead = boolval($bool);
    }

    /**
     *  验证是否开启了事务
     *
     * @author liang<lang@vip.deyi.com>
     */
    public static function isTransaction()
    {
        if (!static::$openTransaction) {
            throw new \Exception('必须开启事物方可执行本请求', ExceptionConstant::INTERNAL_ERROR);
        }
    }

    /**
     * 开启事务
     *
     * @author liang<lang@vip.deyi.com>
     */
    public static function beginTransaction()
    {
        if (!static::$openTransaction) {

            static::$openTransaction = true;

            if (empty(static::$transConnPool)) {
                foreach (static::$_dbWriteConPool as $dbName => $dbCon) {
                    static::$transConnPool[$dbName] = $dbCon;
                    $dbCon->begin();
                }
            }
        }
    }

    /**
     *  提交数据库事物
     *
     * @author liang<lang@vip.deyi.com>
     * @return boolean
     */
    public static function commitTransaction()
    {
        if (!static::$openTransaction) {
            return true;
        }

        // 一个页面中只能开启一次事物提交,避免二次提交
        static::$openTransaction = false;

        foreach (static::$transConnPool as $db => $conn) {
            try {
                if (!$conn->commit()) {
                    static::$_logger->error('transaction commit failed');

                    return false;
                }
                //提交成功就删除这个数据库的连接池
                unset(static::$transConnPool[$db]);
            } catch (\Exception $e) {
                static::$_logger->error('transaction failed: ' . $e->getMessage());

                return false;
            }
        }

        return true;
    }

    /**
     *  回滚数据库事物
     *
     * @author liang<lang@vip.deyi.com>
     * @return bool
     */
    public static function rollbackTransaction()
    {
        if (!static::$openTransaction) {
            return true;
        }

        // 一个页面中只能开启一次事物回滚,避免二次回滚
        static::$openTransaction = false;

        foreach (static::$transConnPool as $conn) {
            if (!$conn->rollback()) {
                static::$_logger->error('transaction rollback failed');

                return false;
            }
        }

        return true;
    }

    /**
     *
     * 采用ORM写入数据前的动作
     *
     * @author liang<lang@vip.deyi.com>
     * @since  2015年5月22日
     */
    protected function beforeSave()
    {
        if (empty(static::$_propCache[static::$_TABLENAME])) {
            $vars = get_object_vars($this);

            if ($vars == null) {
                return;
            }

            foreach (array_keys($vars) as $prop) {
                if ((is_string($this->$prop) || is_null($this->$prop)) && (strpos($prop, '_') !== 0)) {
                    static::$_propCache[static::$_TABLENAME][] = $prop;
                }
            }
        }

        if (static::$_charset[static::$_DBNAME] !== 'gbk') {
            return;
        }

        foreach (static::$_propCache[static::$_TABLENAME] as $prop) {
            if (isset($this->$prop)) {
                $this->$prop = mb_convert_encoding($this->$prop, 'gbk', 'utf8');
            }
        }
    }

    /**
     *
     * 获取数据前的处理动作
     *
     * @author liang<lang@vip.deyi.com>
     * @since  2015年5月22日
     */
    protected function afterFetch()
    {
        if (empty(static::$_propCache[static::$_TABLENAME])) {
            $vars = get_object_vars($this);

            if ($vars === null) {
                return;
            }

            foreach (array_keys($vars) as $prop) {
                if ((is_string($this->$prop) || is_null($this->$prop)) && (strpos($prop, '_') !== 0)) {
                    static::$_propCache[static::$_TABLENAME][] = $prop;
                }
            }
        }

        if (static::$_charset[static::$_DBNAME] !== 'gbk') {
            return;
        }

        foreach (static::$_propCache[static::$_TABLENAME] as $prop) {
            if (isset($this->$prop)) {
                $this->$prop = mb_convert_encoding($this->$prop, 'utf8', 'gbk');
            }
        }
    }

    /**
     *
     * 原生SQL执行update，delete，insert操作调用方法
     *
     * @author liang<lang@vip.deyi.com>
     * @since  2015年5月22日
     *
     * @param string $sqlStatement
     * @param array $placeholders
     * @param array $dataTypes
     *
     * @return boolean
     */
    protected function execute($sqlStatement, $placeholders = null, $dataTypes = null)
    {
        if (static::$_charset[static::$_DBNAME] === 'gbk' && is_array($placeholders)) {
            foreach ($placeholders as &$v) {
                $v = mb_convert_encoding($v, 'gbk', 'utf8');
            }
        }
        // remove key
        $placeholders = is_array($placeholders) ? array_values($placeholders) : $placeholders;

        try {
            return @$this->getWriteConnection()->execute($sqlStatement, $placeholders, $dataTypes);
        } catch (\PDOException $e) {
            if (strpos($e->getMessage(), 'MySQL server has gone away') || strpos($e->getMessage(), 'Connection timed out')) {
                $this->getWriteConnection()->close();
                $this->getWriteConnection()->connect();

                static::$_logger->info('MYSQL EXECUTE RECONNECT OK, SQL:' . $sqlStatement . ' VALUES: ' . (is_array($placeholders) ? implode(',',
                        $placeholders) : ''));

                return $this->getWriteConnection()->execute($sqlStatement, $placeholders, $dataTypes);
            }
            static::$_logger->error('MySQL EXECUTE FAILED, SQL: ' . $sqlStatement . ' VALUES: ' . (is_array($placeholders) ? implode(',', $placeholders) : ''));
            throw $e;
        }

    }

    /**
     *  原生SQL执行select操作调用方法
     *
     * @author liang<lang@vip.deyi.com>
     * @since  2015年5月22日
     *
     * @param string $sqlStatement
     * @param array $placeholders
     * @param array $dataTypes
     *
     * @return ResultSet
     */
    protected function select($sqlStatement, $placeholders = null, $dataTypes = null)
    {
        if (static::$_charset[static::$_DBNAME] === 'gbk' && is_array($placeholders)) {
            foreach ($placeholders as &$v) {
                $v = mb_convert_encoding($v, 'gbk', 'utf8');
            }
        }

        $subClass = get_class($this);
        // remove key
        if ($placeholders) {
            $placeholders = array_values($placeholders);
        }

        try {
            if (static::$openTransaction) {
                $result = @$this->getWriteConnection()->query($sqlStatement, $placeholders, $dataTypes);
            } else {
                $result = @$this->getReadConnection()->query($sqlStatement, $placeholders, $dataTypes);
            }

            return new ResultSet(null, new $subClass(), $result);
        } catch (\PDOException $e) {
            if (strpos($e->getMessage(), 'MySQL server has gone away') || strpos($e->getMessage(), 'Connection timed out')) {
                $this->getReadConnection()->close();
                $this->getReadConnection()->connect();
                $result = $this->getReadConnection()->query($sqlStatement, $placeholders, $dataTypes);

                static::$_logger->info('MYSQL SELECT RECONNECT OK, SQL:' . $sqlStatement . ' VALUES: ' . (is_array($placeholders) ? implode(',',
                        $placeholders) : ''));

                return new ResultSet(null, new $subClass(), $result);
            }
            static::$_logger->error('MySQL SELECT FAILED, SQL: ' . $sqlStatement . ' VALUES: ' . (is_array($placeholders) ? implode(',', $placeholders) : ''));

            if (strpos($this->getReadConnectionService(), '_slave') !== false) {
                apcu_store(CacheKeyConstant::DB_SLAVE_SKIP_CACHE_KEY, 1, CacheTimeoutConstant::LOCAL_CACHE);
            }

            throw $e;
        }
    }

    /**
     *  获取最后插入的数据自增ID值
     *
     * @author liang<lang@vip.deyi.com>
     * @return int
     */
    public function getLastId()
    {
        return $this->getWriteConnection()->lastInsertId();
    }

    /**
     *  获取一条数据
     *
     * @author liang<lang@vip.deyi.com>
     *
     * @param      $sqlStatement
     * @param null $placeholders
     * @param null $dataTypes
     *
     * @return bool|\Phalcon\Mvc\ModelInterface
     */
    protected function selectOne($sqlStatement, $placeholders = null, $dataTypes = null)
    {
        $ret = self::select($sqlStatement, $placeholders, $dataTypes);

        return $ret ? $ret->getFirst() : false;
    }


    /**
     * 获取最近execute的SQL影响行数
     *
     * @author liang<lang@vip.deyi.com>
     * @return int
     */
    public function getAffectRows()
    {
        return $this->getWriteConnection()->affectedRows();
    }

    /**
     *  获取当前调用模型的表明
     *
     * @author liang<lang@vip.deyi.com>
     * @return null
     */
    public static function getTableName()
    {
        return static::$_TABLENAME;
    }

    /**
     *  获取指定表下关联的字段名
     *
     * @author liang<lang@vip.deyi.com>
     *
     * @param $tablename
     *
     * @return mixed
     */
    public static function getPropByTableName($tablename)
    {
        return static::$_propCache[$tablename];
    }

    /**
     *  组装SQL中LIMIT条件
     *
     * @author liang<lang@vip.deyi.com>
     *
     * @param $offset
     * @param $limit
     *
     * @return string
     */
    protected function assembleLimit($offset, $limit)
    {
        if (is_null($offset)) {
            return '';
        }
        if (is_null($limit)) {
            return ' LIMIT ' . $offset;
        }

        return ' LIMIT ' . (int)$offset . ',' . (int)$limit;
    }

    /**
     *  组装普通条件查询SQL(and组合)
     *
     * @author liang<lang@vip.deyi.com>
     * @param $condition
     * @param bool $prefix 是否带有where
     * @return string
     */
    protected function assembleNormalCondition($condition, $prefix = true)
    {
        if (empty($condition)) {
            return ' ';
        }

        if ($prefix) {
            return ' WHERE ' . implode(' AND ', $condition) . ' ';
        }

        return ' ( ' . implode(' AND ', $condition) . ' ) ';
    }

    /**
     *  组装普通条件查询SQL(or组合,仅供后台查询使用)
     *
     * @author liang<lang@vip.deyi.com>
     * @param $condition
     * @param bool $prefix 是否带有where
     * @return string
     */
    protected function assembleOrCondition($condition, $prefix = true)
    {
        if (empty($condition)) {
            return ' ';
        }

        if ($prefix) {
            return ' WHERE ' . implode(' OR ', $condition) . ' ';
        }

        return ' ( ' . implode(' OR ', $condition) . ' ) ';
    }

    /**
     * 组装复杂WHERE条件SQL语句（仅供后台查询使用）
     *
     * @author liang<lang@vip.deyi.com>
     * @param $conds
     * @param $likeConds
     * @param $connect
     * @return string
     */
    protected function assembleComplexWhereSql($conds, $likeConds, $connect = 'AND')
    {
        $where = ' ';

        if (!empty($conds) && !empty($likeConds)) {
            $where = $this->assembleNormalCondition($conds, true) . ' ' . $connect . ' ' . $this->assembleOrCondition($likeConds, false);
        } elseif (empty($conds) && !empty($likeConds)) {
            $where = $this->assembleOrCondition($likeConds, true);
        } elseif (!empty($conds) && empty($likeConds)) {
            $where = $this->assembleNormalCondition($conds, true);
        }

        return $where;
    }

    /**
     *  组装SQL占位符
     *
     * @author liang<lang@vip.deyi.com>
     *
     * @param $arr
     *
     * @return string
     */
    protected function assemblePlaceholders($arr)
    {
        $placeholders = [];
        foreach ($arr as $v) {
            $placeholders[] = '?';
        }

        return ' (' . implode(',', $placeholders) . ') ';
    }

    /**
     *  获取SQL COUNT统一调用方法
     *
     * @author liang<lang@vip.deyi.com>
     *
     * @param      $sqlStatement
     * @param null $placeholders
     * @param null $dataTypes
     *
     * @return mixed
     */
    protected function selectCount($sqlStatement, $placeholders = null, $dataTypes = null)
    {
        $result = $this->selectOne($sqlStatement, $placeholders, $dataTypes);
        if ($result) {
            $num = floatval($result->num);
            static::$_propCache[static::$_TABLENAME] = [];

            return $num;
        }

        static::$_propCache[static::$_TABLENAME] = [];

        return 0;
    }

    /**
     *  获取单个指定字段值
     *
     * @author liang<lang@vip.deyi.com>
     * @param $sqlStatement
     * @param null $placeholders
     * @return bool
     */
    protected function getOneField($sqlStatement, $placeholders = null)
    {

        $result = $this->selectOne($sqlStatement, $placeholders, null);
        if ($result) {
            $field = $result->field;
            static::$_propCache[static::$_TABLENAME] = [];

            return $field;
        }

        static::$_propCache[static::$_TABLENAME] = [];

        return false;
    }

    /**
     * 获取指定列数据
     *
     * @author liang<lang@vip.deyi.com>
     *
     * @param      $sqlStatement
     * @param      $fields
     * @param null $placeholders
     * @param null $dataTypes
     *
     * @return array
     */
    protected function selectCols($sqlStatement, $fields, $placeholders = null, $dataTypes = null)
    {
        $result = $this->select($sqlStatement, $placeholders, $dataTypes);

        if (count($result) > 0) {
            $list = [];
            foreach ($result as $v) {
                $obj = new \stdClass();
                foreach ($fields as $field) {
                    $obj->$field = $v->$field;
                }
                $list[] = $obj;
            }

            static::$_propCache[static::$_TABLENAME] = [];

            unset($result);

            return $list;
        }

        return [];
    }

    /**
     * 获取单条指定列数据
     *
     * @author liang<lang@vip.deyi.com>
     * @param $sqlStatement
     * @param $fields
     * @param null $placeholders
     * @param null $dataTypes
     * @return mixed|null
     */
    protected function selectOneCols($sqlStatement, $fields, $placeholders = null, $dataTypes = null)
    {
        $result = $this->selectCols($sqlStatement, $fields, $placeholders, $dataTypes);

        if (empty($result)) {
            return null;
        }

        return $result[0];
    }

    /**
     * 获取单字段数据列表
     *
     * @author liang<lang@vip.deyi.com>
     * @param $sqlStatement
     * @param $field
     * @param null $placeholders
     * @param null $dataTypes
     * @return array
     */
    protected function selectOneCol($sqlStatement, $field, $placeholders = null, $dataTypes = null)
    {
        $result = $this->select($sqlStatement, $placeholders, $dataTypes);

        if (count($result) > 0) {
            $list = [];

            foreach ($result as $v) {
                $list[] = $v->$field;
            }

            static::$_propCache[static::$_TABLENAME] = [];

            unset($result);

            return $list;
        }

        return [];
    }

    /**
     *  根据$where条件修改表中指定字段$fields数据
     *
     * @author liang<lang@vip.deyi.com>
     * @param array $fields
     * @param array $where
     * @return bool
     */
    public function updateByFields($fields, $where)
    {

        if (empty($fields) || !is_array($fields)) {
            return false;
        }

        $sets = [];
        $values = [];
        foreach ($fields as $k => $v) {
            // key末尾为+号或者-号的时候对此字段做加处理
            if (preg_match('/^([a-zA-Z0-9_]+)([\+|\-])$/', $k, $match)) {
                $sets[] = $match[1] . ' = ' . $match[1] . $match[2] . '? ';
                $values[] = $v;
            } else {
                $sets[] = $k . ' = ? ';
                $values[] = $v;
            }
        }

        $wheres = [];
        foreach ($where as $k => $v) {
            if (preg_match('/^([a-zA-Z0-9_]+)%$/', $k, $match)) {
                $wheres[] = ' ' . str_replace('%', '', $k) . ' LIKE CONCAT("%",?,"%") ';
                $values[] = $v;
            } elseif (preg_match('/^([a-zA-Z0-9_]+)(<=|<|>|>=)$/', $k, $match)) {
                $wheres[] = $match[1] . $match[2] . '? ';
                $values[] = $v;
            } else {
                $wheres[] = $k . ' = ?';
                $values[] = $v;
            }
        }

        $sql = 'UPDATE ' . static::$_TABLENAME . ' SET ' . implode(',', $sets) . ' WHERE ' . implode(' AND ', $wheres);

        return $this->execute($sql, $values);
    }

    /**
     * 设定指定字段为NULL值
     * @author lang@vip.deyi.com
     * @param $field
     * @param $where
     * @return bool
     */
    public function setNullByField($field, $where)
    {

        $wheres = [];
        $values = [];
        foreach ($where as $k => $v) {
            if (preg_match('/^([a-zA-Z0-9_]+)%$/', $k, $match)) {
                $wheres[] = ' ' . str_replace('%', '', $k) . ' LIKE CONCAT("%",?,"%") ';
                $values[] = $v;
            } else {
                $wheres[] = $k . ' = ?';
                $values[] = $v;
            }
        }

        $sql = 'UPDATE ' . static::$_TABLENAME . ' SET ' . $field . '=NULL WHERE ' . implode(' AND ', $wheres);

        return $this->execute($sql, $values);
    }

    /**
     *  执行insert语句插入指定fields到表数据中
     *
     * @author liang<lang@vip.deyi.com>
     * @param $fields
     * @param boolean $replace 是否使用replace语句
     * @param boolean $low 是否降低写入优先级
     * @return bool
     */
    public function insertByFields($fields, $replace = false, $low = false)
    {
        if (empty($fields) || !is_array($fields)) {
            return false;
        }

        $values = array_values($fields);
        $fieldNames = implode(',', array_keys($fields));

        $sql = ($replace ? 'REPLACE ' : 'INSERT ') . ($low ? ' LOW_PRIORITY ' : '') . ' INTO ' . static::$_TABLENAME . '(' . $fieldNames . ') VALUES' . $this->assemblePlaceholders($fields);

        return $this->execute($sql, $values);
    }

    /**
     *  批量INSERT SQL
     *
     * @author liang<lang@vip.deyi.com>
     * @param $fields //字段数组定义
     * @param $values //二维数组和字段对应的写入数据
     * @return bool
     */
    public function batchInsertByFields($fields, $values)
    {
        if (empty($fields) || !is_array($fields)) {
            return false;
        }

        $fieldNames = implode(',', array_values($fields));

        $placeholders = [];
        $vals = [];
        foreach ($values as $v) {
            $placeholders[] = $this->assemblePlaceholders($v);
            $vals = array_merge($vals, $v);
        }

        $sql = 'INSERT INTO ' . static::$_TABLENAME . '(' . $fieldNames . ') VALUES' . implode(',', $placeholders);


        return $this->execute($sql, $vals);
    }

    /**
     *  检查DB是否正常连接
     *
     * @author liang<lang@vip.deyi.com>
     */
    public function checkConnection()
    {
        try {
            @$this->getReadConnection()->query('SELECT 1');
            @$this->getWriteConnection()->query('SELECT 1');
        } catch (\PDOException $e) {
            if (strpos($e->getMessage(), 'MySQL server has gone away')) {
                $this->getReadConnection()->close();
                $this->getReadConnection()->connect();
                $this->getWriteConnection()->close();
                $this->getWriteConnection()->connect();
            }
        }
    }

    /**
     * 方法级事务锁，避免同一时间多个方法被调用
     *
     * @author liang<lang@vip.deyi.com>
     * @param $funcName
     * @param int $timeout
     * @return bool
     */
    protected function serviceTransactionLock($funcName, $timeout = 5)
    {
        $cacheKey = CacheFunction::generateKey(CacheKeyConstant::MUTEX_LOCK_CACHE_KEY, get_called_class() . '\\' . $funcName);

        if (static::$_memcached->add($cacheKey, 1, $timeout)) {
            return true;
        }

        return false;
    }

    /**
     * 方法级事务锁,解锁
     *
     * @author liang<lang@vip.deyi.com>
     * @param $funcName
     * @return bool
     */
    protected function serviceTransactionUnLock($funcName)
    {
        $cacheKey = CacheFunction::generateKey(CacheKeyConstant::MUTEX_LOCK_CACHE_KEY, get_called_class() . '\\' . $funcName);

        return static::$_memcached->delete($cacheKey);
    }

    /**
     *  组装查询条件（支持like查询）
     *
     * @author liang<lang@vip.deyi.com>
     * @param $conditions
     * @return array
     */
    protected function assembleConditionsData($conditions)
    {
        $conds = [];
        $values = [];
        $likeConds = [];
        $likeValues = [];
        foreach ($conditions as $k => $v) {
            if (is_array($v)) {
                $conds[] = ' ' . $k . ' IN ' . $this->assemblePlaceholders($v) . ' ';
                $values = array_merge($values, $v);
            } elseif (strpos($k, '<>')) {
                $conds[] = ' ' . str_replace('<>', '', $k) . ' <> ? ';
                $values[] = $v;
            } elseif (strpos($k, '%')) {
                $likeConds[] = ' ' . str_replace('%', '', $k) . ' LIKE CONCAT("%",?,"%") ';
                $likeValues[] = $v;
            } elseif (strpos($k, '>=')) {
                $conds[] = ' ' . str_replace('>=', '', $k) . ' >= ? ';
                $values[] = $v;
            } elseif (strpos($k, '>')) {
                $conds[] = ' ' . str_replace('>', '', $k) . ' > ? ';
                $values[] = $v;
            } elseif (strpos($k, '<=')) {
                $conds[] = ' ' . str_replace('<=', '', $k) . ' <= ? ';
                $values[] = $v;
            } elseif (strpos($k, '<')) {
                $conds[] = ' ' . str_replace('<', '', $k) . ' < ? ';
                $values[] = $v;
            } elseif (strpos($k, '!=')) {
                $conds[] = ' ' . str_replace('!=', '', $k) . ' != ? ';
                $values[] = $v;
            } else {
                $conds[] = ' ' . $k . ' = ? ';
                $values[] = $v;
            }
        }

        return [array_merge($values, $likeValues), $conds, $likeConds];
    }

    /**
     *  添加图片域名前缀部分
     *
     * @param $urlOrUri
     * @return string
     */
    public static function addImgUrlPrefixDomain(&$urlOrUri)
    {
        if ('' !== $urlOrUri && false == stristr($urlOrUri, 'https:') && false == stristr($urlOrUri, 'http:')) {
            $urlOrUri = static::$_config->project->imageurl . $urlOrUri;
        }

        return $urlOrUri;
    }


    /**
     * 通过一个字段值查找一条数据
     *
     * @param $field
     * @param $value
     * @param bool $useForUpdate
     * @return bool|\Phalcon\Mvc\ModelInterface
     */
    public function getOneByField($field, $value, $useForUpdate = false)
    {
        $sql = 'SELECT * FROM ' . static::$_TABLENAME . ' WHERE ' . $field . ' = ? LIMIT 1'. ($useForUpdate ? ' FOR UPDATE ' : '');

        return $this->selectOne($sql, [$value]);
    }

    /**
     * 通过多个字段条件查找一条数据
     *
     * @param $conds
     * @param $useForUpdate
     * @return bool|\Phalcon\Mvc\ModelInterface
     */
    public function getOneByFields($conds, $useForUpdate = false)
    {
        $fields = array_keys($conds);
        $values = array_values($conds);

        $where = [];
        foreach ($fields as $f) {
            $where[] = ' ' . $f . '=? ';
        }
        $sql = 'SELECT * FROM ' . static::$_TABLENAME . ' WHERE ' . implode(' AND ', $where) . ' LIMIT 1 ' . ($useForUpdate ? ' FOR UPDATE ' : '');

        return $this->selectOne($sql, $values);
    }

    /**
     * 通过一个字段值删除一条数据
     *
     * @author liang<lang@vip.deyi.com>
     * @param $field
     * @param $value
     * @return bool
     */
    public function deleteByField($field, $value)
    {
        $sql = 'DELETE FROM ' . static::$_TABLENAME . ' WHERE ' . $field . ' = ?';

        return $this->execute($sql, [$value]);
    }

    /**
     * 通过多个字段条件值删除一条数据
     *
     * @author liang<lang@vip.deyi.com>
     * @param $conds
     * @return bool
     */
    public function deleteByFields($conds)
    {
        $fields = array_keys($conds);
        $values = array_values($conds);

        $where = '';
        foreach ($fields as $f) {
            $where[] = ' ' . $f . '=? ';
        }
        $sql = 'DELETE FROM ' . static::$_TABLENAME . ' WHERE ' . implode(' AND ', $where);

        return $this->execute($sql, $values);
    }

}
